{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# RUL estimation UNIBO Powertools Dataset"
   ],
   "metadata": {
    "id": "jw9FMur02UtZ"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import scipy.io\n",
    "import math\n",
    "import os\n",
    "import ntpath\n",
    "import sys\n",
    "import logging\n",
    "import time\n",
    "import sys\n",
    "import random\n",
    "\n",
    "from importlib import reload\n",
    "import plotly.graph_objects as go\n",
    "\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras import layers, regularizers\n",
    "\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Dropout, Activation\n",
    "from tensorflow.keras.optimizers import SGD, Adam\n",
    "from tensorflow.keras.layers import LSTM, Embedding, RepeatVector, TimeDistributed, Masking, Bidirectional\n",
    "from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, LambdaCallback\n",
    "\n",
    "\n",
    "IS_COLAB = False\n",
    "IS_TRAINING = True\n",
    "RESULT_NAME = \"\"\n",
    "\n",
    "if IS_COLAB:\n",
    "    from google.colab import drive\n",
    "    drive.mount('/content/drive')\n",
    "    data_path = \"/content/drive/My Drive/battery-state-estimation/battery-state-estimation/\"\n",
    "else:\n",
    "    data_path = \"../../\"\n",
    "\n",
    "sys.path.append(data_path)\n",
    "from data_processing.unibo_powertools_data import UniboPowertoolsData, CycleCols\n",
    "from data_processing.model_data_handler import ModelDataHandler\n",
    "from data_processing.prepare_rul_data import RulHandler"
   ],
   "outputs": [],
   "metadata": {
    "id": "XKxZ90kO2Uta"
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Config logging"
   ],
   "metadata": {
    "id": "MfVCRISs2Utc"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "reload(logging)\n",
    "logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.DEBUG, datefmt='%Y/%m/%d %H:%M:%S')"
   ],
   "outputs": [],
   "metadata": {
    "id": "K2IvySBk2Utd"
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Load Data"
   ],
   "metadata": {
    "id": "KsbkwTX22Utf"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "dataset = UniboPowertoolsData(\n",
    "    test_types=[],\n",
    "    chunk_size=1000000,\n",
    "    lines=[37, 40],\n",
    "    charge_line=37,\n",
    "    discharge_line=40,\n",
    "    base_path=data_path\n",
    ")"
   ],
   "outputs": [],
   "metadata": {
    "id": "DrHYRy-a2Utg"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "train_names = [\n",
    "    '000-DM-3.0-4019-S',#minimum capacity 1.48\n",
    "    '001-DM-3.0-4019-S',#minimum capacity 1.81\n",
    "    '002-DM-3.0-4019-S',#minimum capacity 2.06\n",
    "\n",
    "    '009-DM-3.0-4019-H',#minimum capacity 1.41\n",
    "    '010-DM-3.0-4019-H',#minimum capacity 1.44\n",
    "\n",
    "    '014-DM-3.0-4019-P',#minimum capacity 1.7\n",
    "    '015-DM-3.0-4019-P',#minimum capacity 1.76\n",
    "    '016-DM-3.0-4019-P',#minimum capacity 1.56\n",
    "    '017-DM-3.0-4019-P',#minimum capacity 1.29\n",
    "    #'047-DM-3.0-4019-P',#new 1.98\n",
    "    #'049-DM-3.0-4019-P',#new 2.19\n",
    "\n",
    "\n",
    "\n",
    "    '007-EE-2.85-0820-S',#2.5\n",
    "    '008-EE-2.85-0820-S',#2.49\n",
    "    '042-EE-2.85-0820-S',#2.51\n",
    "\n",
    "    '043-EE-2.85-0820-H',#2.31\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "    '018-DP-2.00-1320-S',#minimum capacity 1.82\n",
    "    #'019-DP-2.00-1320-S',#minimum capacity 1.61\n",
    "    '036-DP-2.00-1720-S',#minimum capacity 1.91\n",
    "    '037-DP-2.00-1720-S',#minimum capacity 1.84\n",
    "    '038-DP-2.00-2420-S',#minimum capacity 1.854 (to 0)\n",
    "    '050-DP-2.00-4020-S',#new 1.81\n",
    "    '051-DP-2.00-4020-S',#new 1.866\n",
    "\n",
    "\n",
    "    \n",
    "    '040-DM-4.00-2320-S',#minimum capacity 3.75, cycles 188\n",
    "]\n",
    "\n",
    "test_names = [\n",
    "    '003-DM-3.0-4019-S',#minimum capacity 1.84\n",
    "\n",
    "    '011-DM-3.0-4019-H',#minimum capacity 1.36\n",
    "\n",
    "    '013-DM-3.0-4019-P',#minimum capacity 1.6\n",
    "\n",
    "\n",
    "\n",
    "    '006-EE-2.85-0820-S',# 2.621\n",
    "    \n",
    "    '044-EE-2.85-0820-H',# 2.43\n",
    "\n",
    "\n",
    "\n",
    "    '039-DP-2.00-2420-S',#minimum capacity 1.93\n",
    "\n",
    "\n",
    "\n",
    "    '041-DM-4.00-2320-S',#minimum capacity 3.76, cycles 190\n",
    "]"
   ],
   "outputs": [],
   "metadata": {
    "id": "NSFp-2Rl2Utj"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "dataset.prepare_data(train_names, test_names)\n",
    "dataset_handler = ModelDataHandler(dataset, [\n",
    "    CycleCols.VOLTAGE,\n",
    "    CycleCols.CURRENT,\n",
    "    CycleCols.TEMPERATURE\n",
    "])\n",
    "\n",
    "rul_handler = RulHandler()"
   ],
   "outputs": [],
   "metadata": {
    "id": "k-yTrXQ12Utm"
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Data preparation"
   ],
   "metadata": {
    "id": "bAAxueIqkZM9"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "CAPACITY_THRESHOLDS = {\n",
    "  3.0 : 2.7,#th 90% - min 2.1, 70%\n",
    "  2.85 : 2.7,#th 94.7% - min 2.622, 92%\n",
    "  2.0 : 1.93,#th 96.5% - min 1.93, 96.5%\n",
    "  4.0 : 3.77,#th 94.2% - min 3.77 94.2%\n",
    "  4.9 : 4.7,#th 95.9% - min 4.3, 87.7%\n",
    "  5.0 : 4.5#th 90% - min 3.63, 72.6%\n",
    "}\n",
    "N_CYCLE = 500\n",
    "WARMUP_TRAIN = 15\n",
    "WARMUP_TEST = 30\n",
    "\n",
    "(train_x, train_y_soh, test_x, test_y_soh,\n",
    "  train_battery_range, test_battery_range,\n",
    "  time_train, time_test, current_train, current_test) = dataset_handler.get_discharge_whole_cycle_future(train_names, test_names)\n",
    "\n",
    "train_y = rul_handler.prepare_y_future(train_names, train_battery_range, train_y_soh, current_train, time_train, CAPACITY_THRESHOLDS)\n",
    "del globals()[\"current_train\"]\n",
    "del globals()[\"time_train\"]\n",
    "test_y = rul_handler.prepare_y_future(test_names, test_battery_range, test_y_soh, current_test, time_test, CAPACITY_THRESHOLDS)\n",
    "del globals()[\"current_test\"]\n",
    "del globals()[\"time_test\"]\n",
    "train_x, test_x = rul_handler.compress_cycle(train_x, test_x)\n",
    "\n",
    "\n",
    "x_norm = rul_handler.Normalization()\n",
    "train_x, test_x = x_norm.normalize(train_x, test_x)\n",
    "train_x = rul_handler.battery_life_to_time_series(train_x, N_CYCLE, train_battery_range)\n",
    "test_x = rul_handler.battery_life_to_time_series(test_x, N_CYCLE, test_battery_range)\n",
    "\n",
    "train_x, train_y, train_battery_range, train_y_soh = rul_handler.delete_initial(train_x, train_y, train_battery_range, train_y_soh, WARMUP_TRAIN)\n",
    "test_x, test_y, test_battery_range, test_y_soh = rul_handler.delete_initial(test_x, test_y, test_battery_range, test_y_soh, WARMUP_TEST)\n",
    "\n",
    "# first one is SOH, we keep only RUL\n",
    "train_y = train_y[:,1]\n",
    "test_y = test_y[:,1]"
   ],
   "outputs": [],
   "metadata": {
    "id": "TbntMzBZkZM9",
    "tags": []
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Y normalization"
   ],
   "metadata": {
    "id": "GskElnOAxTil"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "y_norm = rul_handler.Normalization()\n",
    "train_y, test_y = y_norm.normalize(train_y, test_y)"
   ],
   "outputs": [],
   "metadata": {
    "id": "5EOallNnxSpQ"
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Model training"
   ],
   "metadata": {
    "id": "7iYU-n0K2Utq"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "if IS_TRAINING:\n",
    "    EXPERIMENT = \"lstm_rul_unibo_powertools\"\n",
    "\n",
    "    experiment_name = time.strftime(\"%Y-%m-%d-%H-%M-%S\") + '_' + EXPERIMENT\n",
    "    print(experiment_name)\n",
    "\n",
    "    # Model definition\n",
    "\n",
    "    opt = tf.keras.optimizers.Adam(lr=0.000003)\n",
    "\n",
    "    model = Sequential()\n",
    "    model.add(Masking(input_shape=(train_x.shape[1], train_x.shape[2])))\n",
    "    model.add(LSTM(128, activation='selu',\n",
    "                    return_sequences=True,\n",
    "                    kernel_regularizer=regularizers.l2(0.0002)))\n",
    "    model.add(LSTM(64, activation='selu', return_sequences=False,\n",
    "                    kernel_regularizer=regularizers.l2(0.0002)))\n",
    "    model.add(Dense(64, activation='selu', kernel_regularizer=regularizers.l2(0.0002)))\n",
    "    model.add(Dense(32, activation='selu', kernel_regularizer=regularizers.l2(0.0002)))\n",
    "    model.add(Dense(1, activation='linear'))\n",
    "    model.summary()\n",
    "\n",
    "    model.compile(optimizer=opt, loss='huber', metrics=['mse', 'mae', 'mape', tf.keras.metrics.RootMeanSquaredError(name='rmse')])"
   ],
   "outputs": [],
   "metadata": {
    "id": "LSx96n4w2Uts"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "if IS_TRAINING:\n",
    "    history = model.fit(train_x, train_y, \n",
    "                                epochs=500, \n",
    "                                batch_size=32, \n",
    "                                verbose=1,\n",
    "                                validation_split=0\n",
    "                               )"
   ],
   "outputs": [],
   "metadata": {
    "id": "AIEcv6Ey2Utu"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "if IS_TRAINING:\n",
    "    model.save(data_path + 'results/trained_model/%s.h5' % experiment_name)\n",
    "\n",
    "    hist_df = pd.DataFrame(history.history)\n",
    "    hist_csv_file = data_path + 'results/trained_model/%s_history.csv' % experiment_name\n",
    "    with open(hist_csv_file, mode='w') as f:\n",
    "        hist_df.to_csv(f)\n",
    "    history = history.history"
   ],
   "outputs": [],
   "metadata": {
    "id": "oNHlqcvP2Utx"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "if not IS_TRAINING:\n",
    "    history = pd.read_csv(data_path + 'results/trained_model/%s_history.csv' % RESULT_NAME)\n",
    "    model = keras.models.load_model(data_path + 'results/trained_model/%s.h5' % RESULT_NAME)\n",
    "    model.summary()"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "if not IS_TRAINING:\n",
    "    with pd.option_context('display.max_rows', None, 'display.max_columns', None):\n",
    "        print(history)"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Testing"
   ],
   "metadata": {
    "id": "LH5RANQIEQVx"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "results = model.evaluate(test_x, test_y, return_dict = True)\n",
    "print(results)\n",
    "max_rmse = 0\n",
    "for index in range(test_x.shape[0]):\n",
    "    result = model.evaluate(np.array([test_x[index, :, :]]), np.array([test_y[index]]), return_dict = True, verbose=0)\n",
    "    max_rmse = max(max_rmse, result['rmse'])\n",
    "print(\"Max rmse: {}\".format(max_rmse))"
   ],
   "outputs": [],
   "metadata": {
    "id": "ggNKW-VqENFN"
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Results Visualization"
   ],
   "metadata": {
    "id": "uiqyD8Bn2Utz"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "fig = go.Figure()\n",
    "fig.add_trace(go.Scatter(y=history['loss'],\n",
    "                    mode='lines', name='train'))\n",
    "fig.update_layout(title='Loss trend',\n",
    "                  xaxis_title='epoch',\n",
    "                  yaxis_title='loss',\n",
    "                  width=1400,\n",
    "                  height=600)\n",
    "fig.show()"
   ],
   "outputs": [],
   "metadata": {
    "id": "jH9RrBRN2Utz"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "train_predictions = model.predict(train_x)\n",
    "\n",
    "train_y = y_norm.denormalize(train_y)\n",
    "train_predictions = y_norm.denormalize(train_predictions)"
   ],
   "outputs": [],
   "metadata": {
    "id": "gtLOteXd-d6n"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "a = 0\n",
    "for b in train_battery_range:\n",
    "    fig = go.Figure()\n",
    "    fig.add_trace(go.Scatter(x=train_y_soh[a:b], y=train_predictions[a:b,0],\n",
    "                        mode='lines', name='predicted'))\n",
    "    fig.add_trace(go.Scatter(x=train_y_soh[a:b], y=train_y[a:b],\n",
    "                        mode='lines', name='actual'))\n",
    "    fig.update_layout(title='Results on training',\n",
    "                    xaxis_title='SoH Capacity',\n",
    "                    yaxis_title='Remaining Ah until EOL',\n",
    "                    xaxis={'autorange':'reversed'},\n",
    "                    width=1400,\n",
    "                    height=600)\n",
    "    fig.show()\n",
    "    a = b"
   ],
   "outputs": [],
   "metadata": {
    "id": "ZsYMPQ0i2Ut1"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "a = 0\n",
    "for b in train_battery_range:\n",
    "    fig = go.Figure()\n",
    "    fig.add_trace(go.Scatter(y=train_predictions[a:b,0],\n",
    "                        mode='lines', name='predicted'))\n",
    "    fig.add_trace(go.Scatter(y=train_y[a:b],\n",
    "                        mode='lines', name='actual'))\n",
    "    fig.update_layout(title='Results on training',\n",
    "                    xaxis_title='Cycle',\n",
    "                    yaxis_title='Remaining Ah until EOL',\n",
    "                    width=1400,\n",
    "                    height=600)\n",
    "    fig.show()\n",
    "    a = b"
   ],
   "outputs": [],
   "metadata": {
    "id": "Pe1Pzp04J1b5"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "test_predictions = model.predict(test_x)\n",
    "\n",
    "test_y = y_norm.denormalize(test_y)\n",
    "test_predictions = y_norm.denormalize(test_predictions)"
   ],
   "outputs": [],
   "metadata": {
    "id": "m0olyqr4-8BG"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "a = 0\n",
    "for b in test_battery_range:\n",
    "    fig = go.Figure()\n",
    "    fig.add_trace(go.Scatter(x=test_y_soh[a:b], y=test_predictions[a:b,0],\n",
    "                        mode='lines', name='predicted'))\n",
    "    fig.add_trace(go.Scatter(x = test_y_soh[a:b], y=test_y[a:b],\n",
    "                        mode='lines', name='actual'))\n",
    "    fig.update_layout(title='Results on testing',\n",
    "                    xaxis_title='SoH Capacity',\n",
    "                    yaxis_title='Remaining Ah until EOL',\n",
    "                    xaxis={'autorange':'reversed'},\n",
    "                    width=1400,\n",
    "                    height=600)\n",
    "    fig.show()\n",
    "    a = b"
   ],
   "outputs": [],
   "metadata": {
    "id": "9U1MbGnq2Ut4"
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "a = 0\n",
    "for b in test_battery_range:\n",
    "    fig = go.Figure()\n",
    "    fig.add_trace(go.Scatter(y=test_predictions[a:b, 0],\n",
    "                        mode='lines', name='predicted'))\n",
    "    fig.add_trace(go.Scatter(y=test_y[a:b],\n",
    "                        mode='lines', name='actual'))\n",
    "    fig.update_layout(title='Results on testing',\n",
    "                    xaxis_title='Cycle',\n",
    "                    yaxis_title='Remaining Ah until EOL',\n",
    "                    width=1400,\n",
    "                    height=600)\n",
    "    fig.show()\n",
    "    a = b"
   ],
   "outputs": [],
   "metadata": {
    "id": "uLEl8nALKAgJ"
   }
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "lstm_soh_prediction_only_future.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "name": "python382jvsc74a57bd031f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6",
   "display_name": "Python 3.8.2 64-bit"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  },
  "metadata": {
   "interpreter": {
    "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}